//
// Created by Administrator on 2024/5/23.
//
#include "GLFW/glfw3native.h"
#include "GLFW/glfw3.h"
#include "LuaGlfwBinder.h"
#include "log.h"

#define GLFW_NAME ("glfw")


static void ErrorHandler(int error, const char *description) {
    LogE("[GLFW] Error {}: {}\n", (int) error, description);
}

template<typename Ptr>
struct GLFWBox {
    lua_State *L;
    Ptr *ptr;
};

template<typename Ptr>
inline static GLFWBox<Ptr> ofGLFWBox(lua_State *L, Ptr *ptr) {
    GLFWBox<Ptr> box{};
    box.L = L;
    box.ptr = ptr;
    return box;
}

typedef GLFWBox<GLFWmonitor> MonitorBox;
typedef sol::optional<MonitorBox> MonitorOptional;

typedef GLFWBox<GLFWwindow> WindowBox;
typedef sol::optional<WindowBox> WindowOptional;


void LuaGlfwBinder::Bind(sol::state &state) {
    auto L = state.lua_state();
    glfwSetErrorCallback(ErrorHandler);
    state.create_named_table(GLFW_NAME);
    auto glfw = state[GLFW_NAME];
    glfw["init"] = []() {
        return glfwInit();
    };
    glfw["create"] = [L](int width, int height, const char *title, MonitorOptional monitor,
                         WindowOptional share) {
        GLFWwindow *ptr = glfwCreateWindow(width, height, title, monitor->ptr, share->ptr);
        if (!ptr) {
            return sol::make_object(L, nullptr);
        }
        return sol::make_object(L, ofGLFWBox(L, ptr));
    };
    glfw["show"] = [](WindowBox win) {
        glfwShowWindow(win.ptr);
    };
    glfw["destroy"] = [](WindowBox win) {
        glfwDestroyWindow(win.ptr);
    };
    glfw["shouldClose"] = [](WindowBox win) {
        return glfwWindowShouldClose(win.ptr);
    };
    glfw["swapBuffers"] = [](WindowBox win) {
        glfwSwapBuffers(win.ptr);
    };
    glfw["setSizeCallback"] = [](WindowBox win, GLFWwindowsizefun callback) {
        return glfwSetWindowSizeCallback(win.ptr, callback);
    };
    glfw["pollEvents"] = &glfwPollEvents;
    glfw["terminate"] = &glfwTerminate;
}